//
//  DPObjCRuntime.h
//  HigherOrderMessaging
//
//  Created by Ofri Wolfus on 03/11/06.
//  Copyright 2006 Ofri Wolfus. All rights reserved.
//

#ifndef _DP_OBJCRUNTIME_H
#define _DP_OBJCRUNTIME_H

#import <UITestingKit/DPMacros.h>

//===============================================

#pragma mark Runtime Versions Compatibility Layer

/*
 * This section contains a compatibility layer between Objective-C 1
 * (MacOS X 10.4) and Objective-C 2.0 (MacOS X 10.5).
 * Mostly, this layer implements the new (v2.0) API for the old
 * (v1) runtime, as thew new API is much cleaner and simpler to
 * work with.
 * In addition, a new type dp_marg_list is defined, that matches
 * the runtime's marg_list type. As both types are interchangeable,
 * you can freely use dp_marg_list instead of marg_list (which is
														 * the recommended way).
 * WARNING: This API wasn't tested under ObjC 2.0 and might not
 * even compile. Use at your own risk!
 */

#ifndef _DP_OBJC_COMPATIBILITY_H
#define _DP_OBJC_COMPATIBILITY_H


/**************************************
*           Objective-C v1           *
**************************************/
//#pragma mark Objective-C I
/* Stuff that exist in the Objective-C 2 runtime, but not in v1 */
#if !__OBJC2__

#import <objc/objc-class.h>
#import <objc/objc-runtime.h>
#import <objc/Protocol.h> // for objc_method_description

//
//  Methods manipulation
//
#define method_getName(m)					(m->method_name)
#define method_getImplementation(m)			(m->method_imp)
#define method_getTypeEncoding(m)			(m->method_types)
#define method_setImplementation(m, imp)	({IMP __r = m->method_imp; m->method_imp = imp; __r;})

DP_EXTERN char *method_copyReturnType(Method m);
DP_EXTERN char *method_copyArgumentType(Method m, unsigned int index);
DP_EXTERN void method_getReturnType(Method m, char *dst, size_t dst_len);
DP_EXTERN void method_getArgumentType(Method m, unsigned int index,
									  char *dst, size_t dst_len);

// struct objc_method_description *method_getDescription(Method m)
#define method_getDescription(m) \
({ \
	struct objc_method_description __desc; \
	__desc.name = method_getName(m); \
	__desc.types = method_getTypeEncoding(m); \
	&__desc; \
})

//
//  Class manipulation
//
#define class_getName(cls)			(((Class)cls)->name)
#define class_getSuperclass(cls)	(((Class)cls)->super_class)
#define class_getInstanceSize(cls)	( (size_t)(((Class)cls)->instance_size) )
DP_EXTERN Method * class_copyMethodList(Class cls, unsigned int *outCount);
DP_EXTERN BOOL class_addMethod(Class cls, SEL name, IMP imp, 
							   const char *types);

//
//  Selectors
//
/* Shouldn't this work with the new runtime as well? */
#define sel_isEqual(s1, s2) (s1 == s2)

//
//  Classes and objects inspection
//

#define class_isMetaClass(cls) ((((Class)cls)->info & CLS_META) != 0)
#define object_getClass(obj) (obj->isa)

DP_EXTERN BOOL class_respondsToSelector(Class cls, SEL sel);

//
#define _CLS_IS_CLASS(cls)	((((Class)cls)->info & CLS_CLASS) != 0)
#define _CLS_IS_META(cls)	((((Class)cls)->info & CLS_META) != 0)
#define _CLS_GET_META(cls)	(_CLS_IS_META(cls) ? cls : cls->isa)


//
//  Missing types
//
#define _C_CONST	'r'


/**************************************
*           Objective-C v2           *
**************************************/
//#pragma mark Objective-C II
/* Compatibility with the older Objective-C runtime */
#else // ObjC 2

#include <objc/runtime.h>
#include <objc/message.h>
#include <machine/types.h>	// For uintptr_t
#include <malloc/malloc.h>	// For malloc_size

#endif	// !__OBJC2__


//#pragma mark -

// These are defined in the GNU runtime but not in Apple's runtime,
// although the characters are the same.
#ifndef _C_IN
#define _C_IN			'n'
#endif

#ifndef _C_INOUT
#define _C_INOUT		'N'
#endif

#ifndef _C_OUT
#define _C_OUT			'o'
#endif

#ifndef _C_BYCOPY
#define _C_BYCOPY		'O'
#endif

#ifndef _C_BYREF
#define _C_BYREF		'R'
#endif

#ifndef _C_ONEWAY
#define _C_ONEWAY		'V'
#endif


//
//  Working with marg_list
//

/* PPC and PPC64 */
#if defined(__ppc__) || defined(__ppc64__)

#if __OBJC2__
typedef ppc_marg_list dp_ppc_marg_list;
typedef ppc64_marg_list dp_ppc64_marg_list;
#else
typedef struct {
	double fpParams[13];       // F1..F13
    uintptr_t linkage[6];      // ignored by objc_msgSendv; do not modify
    uintptr_t regParams[8];    // R3..R10; objc_msgSendv ignores R3 and R4
    uintptr_t stackParams[0];  // variable-size
} *dp_ppc_marg_list, *dp_ppc64_marg_list;
#endif

#define dp_marg_size		(13 * sizeof(double) + (6 + 8) * sizeof(uintptr_t))
#define dp_marg_var_space	(8 * sizeof(uintptr_t) /* regParams */)

/* i386 */
#elif defined(__i386__)

#if __OBJC2__
typedef x86_marg_list dp_x86_marg_list;
typedef i386_marg_list dp_i386_marg_list;
#else
typedef struct {
	uintptr_t params[0];       // variable-size
} *dp_x86_marg_list, *dp_i386_marg_list;
#endif

// i386 passes arguments on stack
#define dp_marg_size		0
#define dp_marg_var_space	0

/* x86-64 */
#elif defined(__x86_64__)

#if __OBJC2__
typedef x86_64_marg_list dp_x86_64_marg_list;

#define dp_marg_size		( (sizeof(double) + sizeof(char) * 16) * 8 /* fpParams */\
		+ sizeof(uintptr_t) * 10 /* r10 + rax + linkage + regParams */)
#define dp_marg_var_space	(6 * sizeof(uintptr_t) /* regParams */)

#else	// __ObjC2__
#	error x86-64 is not supported in ObjC 1
#endif

#else	// PPC, PPC64, i386, x86-64
#  error unknown architecture
#endif	// PPC, PPC64, i386, x86-64

/*
 * This is the fixed size of the marg_list.
 * This size plus the size returned from method_getSizeOfArguments()
 * is the total size of the arguments list.
 * Use dp_marg_alloc() for marg_list allocation.
 */
#define dp_marg_prearg_size (dp_marg_size - dp_marg_var_space)


/*
 * If you need to work with marg_list, use dp_marg_list which works on all architectures
 * and both on ObjC 1 and 2.
 * An abstraction layer for modifying the contents of the arguments list is not yet
 * supported, so you'll have to be very careful and read the function calling ABI
 * of your target architectures.
 */
#if defined(__ppc__) || defined(__ppc64__)
typedef dp_ppc_marg_list dp_marg_list;
#elif defined(__i386__)
typedef dp_i386_marg_list dp_marg_list;
#elif defined(__x86_64__)
typedef dp_x86_64_marg_list dp_marg_list;
#else
#  error unknown architecture
#endif

#endif // _DP_OBJC_COMPATIBILITY_H

#pragma mark -
#pragma mark A simplified API for some parts of the runtime

//
//  Size and alignment of types
//
#if __OBJC2__
#define sizeOfType(t...) ({ NSUInteger __s; NSGetSizeAndAlignment(t, &__s, NULL); __s; })
#define alignOfType(t...) ({ NSUInteger __s; NSGetSizeAndAlignment(t, NULL, &__s); __s; })
#else
#define sizeOfType(t...) ({ unsigned int __s; NSGetSizeAndAlignment(t, &__s, NULL); __s; })
#define alignOfType(t...) ({ unsigned int __s; NSGetSizeAndAlignment(t, NULL, &__s); __s; })
#endif


//
//  Type descriptions
//
DP_EXTERN unsigned dp_getNumberOfArguments(const char *typedesc);
DP_EXTERN unsigned dp_getArgumentInfo(const char *typedesc, int argIndex, const  char** type, int*  offset);
DP_EXTERN unsigned dp_getSizeOfArguments(const char *typedesc);


//
//  Classes and objects inspection
//
#define class_isClass(cls) class_isMetaClass(cls->isa)
DP_EXTERN BOOL class_isSubclassOfClass(Class cls, Class superCls);

#define object_isInstance(obj)					class_isClass(obj->isa)
#define object_getClass(obj)					(obj->isa)
#define object_isKindOfClass(obj, superCls)		class_isSubclassOfClass(object_getClass(obj), superCls)

// Handles both objects and classes
DP_EXTERN_INLINE BOOL object_respondsToSelector(id object, SEL sel);

DP_EXTERN_INLINE Method dp_getMethod(id obj, SEL sel);

// Returns a NULL terminated array of classes (it's up to you to release it)
DP_EXTERN_INLINE Class *dp_copyClassList(void);


//
//  Selectors
//
DP_EXTERN_INLINE unsigned int sel_getNumberOfArguments(SEL sel);	// Always takes the self and _cmd arguments in count


//
//  Allocation/copying of marg_list
//

#define dp_marg_malloc(argsSize) malloc(dp_marg_prearg_size + (7 + argsSize & ~7) /* <- huh? */)

#define dp_marg_alloc(method) dp_marg_malloc(method_getSizeOfArguments(method))
#define dp_marg_copy(margs, method) \
	({ marg_list __marg = dp_marg_alloc(method); memcpy(__marg, margs, malloc_size(__marg)); })

//
//  Accessing integral types
//
#define dp_margGetObjectRef(margs, offset, type) \
((type *)((char *)margs + dp_marg_prearg_size + offset))

#define dp_margGetObject(margs, offset, type) \
(*dp_margGetObjectRef(margs, offset, type))

#define dp_margSetObject(margs, offset, type, value) \
( dp_margGetObject(margs, offset, type) = (value) )


//
//  Messaging
//

DP_EXTERN id dp_msgSendv(id target, SEL sel, marg_list args);


#endif //_DP_OBJCRUNTIME_H

